<template>
  <div class="echarts"></div>
</template>

<style>
.echarts {
  width: 100%;
  height: 100%;
}
</style>

<script>
import debounce from "lodash/debounce";
import { addListener, removeListener } from "resize-detector";
const INIT_TRIGGERS = ["theme", "initOptions", "autoresize"];
const REWATCH_TRIGGERS = ["manualUpdate", "watchShallow"];
export default {
  props: {
    options: Object,
    theme: [String, Object],
    initOptions: Object,
    group: String,
    autoresize: Boolean,
    watchShallow: Boolean,
    manualUpdate: Boolean
  },
  data() {
    return {
      lastArea: 0
    };
  },
  watch: {
    group(group) {
      this.chart.group = group;
    }
  },
  created() {
    this.initOptionsWatcher();

    INIT_TRIGGERS.forEach(prop => {
      this.$watch(
        prop,
        () => {
          this.refresh();
        },
        { deep: true }
      );
    });

    REWATCH_TRIGGERS.forEach(prop => {
      this.$watch(prop, () => {
        this.initOptionsWatcher();
        this.refresh();
      });
    });
  },
  mounted() {
    // auto init if `options` is already provided
    if (this.options) {
      this.init();
    }
  },
  activated() {
    if (this.autoresize) {
      this.chart && this.chart.resize();
    }
  },
  methods: {
    // provide an explicit merge option method
    mergeOptions(options, notMerge, lazyUpdate) {
      if (this.manualUpdate) {
        this.manualOptions = options;
      }

      if (!this.chart) {
        this.init(options);
      } else {
        this.delegateMethod("setOption", options, notMerge, lazyUpdate);
      }
    },
    // just delegates window.echarts methods to Vue component
    // use explicit params to reduce transpiled size for now
    appendData(params) {
      this.delegateMethod("appendData", params);
    },
    resize(options) {
      this.delegateMethod("resize", options);
    },
    dispatchAction(payload) {
      this.delegateMethod("dispatchAction", payload);
    },
    convertToPixel(finder, value) {
      return this.delegateMethod("convertToPixel", finder, value);
    },
    convertFromPixel(finder, value) {
      return this.delegateMethod("convertFromPixel", finder, value);
    },
    containPixel(finder, value) {
      return this.delegateMethod("containPixel", finder, value);
    },
    showLoading(type, options) {
      this.delegateMethod("showLoading", type, options);
    },
    hideLoading() {
      this.delegateMethod("hideLoading");
    },
    getDataURL(options) {
      return this.delegateMethod("getDataURL", options);
    },
    getConnectedDataURL(options) {
      return this.delegateMethod("getConnectedDataURL", options);
    },
    clear() {
      this.delegateMethod("clear");
    },
    dispose() {
      this.delegateMethod("dispose");
    },
    delegateMethod(name, ...args) {
      if (!this.chart) {
        this.init();
      }
      return this.chart[name](...args);
    },
    delegateGet(methodName) {
      if (!this.chart) {
        this.init();
      }
      return this.chart[methodName]();
    },
    getArea() {
      return this.$el.offsetWidth * this.$el.offsetHeight;
    },
    init(options) {
      if (this.chart) {
        return;
      }
      // @ts-ignore
      const chart = window.echarts.init(this.$el, this.theme, this.initOptions);

      if (this.group) {
        chart.group = this.group;
      }

      chart.setOption(
        options || this.manualOptions || this.options || {},
        true
      );

      if (this.autoresize) {
        this.lastArea = this.getArea();
        this.__resizeHandler = debounce(
          () => {
            if (this.lastArea === 0) {
              // emulate initial render for initially hidden charts
              this.mergeOptions({}, true);
              this.resize();
              this.mergeOptions(this.options || this.manualOptions || {}, true);
            } else {
              this.resize();
            }
            this.lastArea = this.getArea();
          },
          100,
          { leading: true }
        );
        addListener(this.$el, this.__resizeHandler);
      }

      // Object.defineProperties(this, {
      //   width: {
      //     configurable: true,
      //     get: () => {
      //       return this.delegateGet("getWidth");
      //     }
      //   },
      //   height: {
      //     configurable: true,
      //     get: () => {
      //       return this.delegateGet("getHeight");
      //     }
      //   },
      //   isDisposed: {
      //     configurable: true,
      //     get: () => {
      //       return !!this.delegateGet("isDisposed");
      //     }
      //   },
      //   computedOptions: {
      //     configurable: true,
      //     get: () => {
      //       return this.delegateGet("getOption");
      //     }
      //   }
      // });

      this.chart = chart;
      this.chart.on("click", params => {
        this.$emit("handleClick", params);
      });
    },
    initOptionsWatcher() {
      if (this.__unwatchOptions) {
        this.__unwatchOptions();
        this.__unwatchOptions = null;
      }

      if (!this.manualUpdate) {
        this.__unwatchOptions = this.$watch(
          "options",
          (val, oldVal) => {
            if (!this.chart && val) {
              this.init();
            } else {
              // mutating `options` will lead to merging
              // replacing it with new reference will lead to not merging
              // eg.
              // `this.options = Object.assign({}, this.options, { ... })`
              // will trigger `this.chart.setOption(val, true)
              // `this.options.title.text = 'Trends'`
              // will trigger `this.chart.setOption(val, false)`
              this.chart.setOption(val, val !== oldVal);
            }
          },
          { deep: !this.watchShallow }
        );
      }
    },
    destroy() {
      if (this.autoresize) {
        removeListener(this.$el, this.__resizeHandler);
      }
      this.dispose();
      this.chart = null;
    },
    refresh() {
      if (this.chart) {
        this.destroy();
        this.init();
      }
    }
  },

  connect(group) {
    if (typeof group !== "string") {
      group = group.map(chart => chart.chart);
    }
    // @ts-ignore
    window.echarts.connect(group);
  },
  disconnect(group) {
    // @ts-ignore
    window.echarts.disConnect(group);
  },
  registerMap(mapName, geoJSON, specialAreas) {
    // @ts-ignore
    window.echarts.registerMap(mapName, geoJSON, specialAreas);
  },
  registerTheme(name, theme) {
    // @ts-ignore
    window.echarts.registerTheme(name, theme);
  },
  // @ts-ignore
  graphic: window.echarts.graphic
};
</script>
